<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>二次函数 y=ax² 动画交互演示</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            color: #333;
            line-height: 1.6;
        }
        header {
            text-align: center;
            margin-bottom: 20px;
            padding: 15px;
            background: white;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        h1 {
            color: #2c3e50;
            margin-bottom: 10px;
            text-shadow: 1px 1px 3px rgba(0,0,0,0.1);
            font-size: 1.8em;
        }
        .subtitle {
            color: #7f8c8d;
            font-size: 1em;
        }
        .main-container {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        .left-panel {
            flex: 2.5;
            display: flex;
            flex-direction: column;
        }
        .right-panel {
            flex: 2.5;
            display: flex;
            flex-direction: column;
        }
        .container {
            background-color: white;
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        .canvas-container {
            height: 480px;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            overflow: hidden;
            background-color: #fdfdfd;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 10px;
        }
        canvas {
            display: block;
            max-width: 100%;
            max-height: 100%;
        }
        .controls {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 15px;
        }
        button {
            padding: 12px 16px;
            background: linear-gradient(to bottom, #3498db, #2980b9);
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s;
            font-weight: bold;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            font-size: 14px;
            flex: 1;
            min-width: 80px;
        }
        button:hover {
            background: linear-gradient(to bottom, #2980b9, #2573a7);
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0,0,0,0.15);
        }
        button:active {
            transform: translateY(0);
        }
        button:disabled {
            background: #95a5a6;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }
        .slider-container {
            text-align: center;
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
            margin-bottom: 15px;
        }
        .slider-container label {
            display: block;
            margin-bottom: 8px;
            font-weight: bold;
            color: #2c3e50;
            font-size: 1em;
        }
        input[type="range"] {
            width: 100%;
            height: 8px;
            border-radius: 4px;
            background: #dcdde1;
            outline: none;
        }
        .value-display {
            margin-top: 8px;
            font-weight: bold;
            color: #e74c3c;
            font-size: 1.1em;
        }
        .table-container {
            overflow: auto;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            box-shadow: 0 2px 5px rgba(0,0,0,0.05);
            font-size: 0.85em;
            table-layout: fixed;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: center;
            transition: all 0.5s ease;
            opacity: 0;
            transform: translateY(10px);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        th {
            background-color: #3498db;
            color: white;
            font-weight: bold;
        }
        tr:nth-child(even) {
            background-color: #f2f2f2;
        }
        .status {
            padding: 10px;
            background-color: #ecf0f1;
            border-radius: 6px;
            text-align: center;
            font-weight: bold;
            box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
            font-size: 0.9em;
            margin-top: 10px;
        }
        .instructions {
            background-color: #fff9e6;
            padding: 20px;
            border-radius: 8px;
            border-left: 4px solid #f1c40f;
            font-size: 0.9em;
        }
        .instructions h3 {
            margin-top: 0;
            color: #d35400;
            font-size: 1.1em;
            margin-bottom: 10px;
        }
        .instructions ul {
            padding-left: 20px;
        }
        .instructions li {
            margin-bottom: 8px;
        }
        footer {
            text-align: center;
            margin-top: 10px;
            color: #7f8c8d;
            font-size: 0.8em;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .fade-in {
            animation: fadeIn 0.5s ease forwards;
        }
        /* 分数样式 */
        .fraction {
            display: inline-block;
            text-align: center;
            vertical-align: middle;
        }
        .fraction span {
            display: block;
        }
        .fraction .numerator {
            border-bottom: 1px solid white;
            padding: 0 2px;
        }
        .fraction .denominator {
            padding: 0 2px;
        }
    </style>
</head>
<body>
    <header>
        <h1>二次函数 y=ax² 动画交互演示</h1>
        <p class="subtitle">通过拖动条调整系数a，观察函数图像的变化，体验动画效果</p>
    </header>
    
    <div class="main-container">
        <div class="left-panel">
            <div class="container canvas-container">
                <canvas id="graphCanvas" width="550" height="450"></canvas>
            </div>
        </div>
        
        <div class="right-panel">
            <div class="container">
                <div class="controls">
                    <button id="listBtn">列表</button>
                    <button id="pointsBtn">描点</button>
                    <button id="curveBtn">连线</button>
                    <button id="resetBtn">重置</button>
                </div>
                
                <div class="slider-container">
                    <label for="aSlider">系数 a 的值:</label>
                    <input type="range" id="aSlider" min="-3" max="3" step="0.1" value="1">
                    <div class="value-display">当前 a = <span id="aValue">1</span></div>
                </div>
                
                <div class="status" id="status">准备就绪 - 拖动滑块调整a值，点击按钮查看动画效果</div>
            </div>
            
            <div class="container table-container">
                <table id="valueTable" style="display: none;">
                    <thead>
                        <tr>
                            <th style="width: 12%">x</th>
                            <th style="width: 8%">...</th>
                            <th style="width: 8%">-2</th>
                            <th style="width: 8%">
                                <div class="fraction">
                                    <span class="numerator">-3</span>
                                    <span class="denominator">2</span>
                                </div>
                            </th>
                            <th style="width: 8%">-1</th>
                            <th style="width: 8%">
                                <div class="fraction">
                                    <span class="numerator">-1</span>
                                    <span class="denominator">2</span>
                                </div>
                            </th>
                            <th style="width: 8%">0</th>
                            <th style="width: 8%">
                                <div class="fraction">
                                    <span class="numerator">1</span>
                                    <span class="denominator">2</span>
                                </div>
                            </th>
                            <th style="width: 8%">1</th>
                            <th style="width: 8%">
                                <div class="fraction">
                                    <span class="numerator">3</span>
                                    <span class="denominator">2</span>
                                </div>
                            </th>
                            <th style="width: 8%">2</th>
                            <th style="width: 8%">...</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>y = ax²</td>
                            <td>...</td>
                            <td id="y-2"></td>
                            <td id="y-1_5"></td>
                            <td id="y-1"></td>
                            <td id="y-0_5"></td>
                            <td id="y0"></td>
                            <td id="y0_5"></td>
                            <td id="y1"></td>
                            <td id="y1_5"></td>
                            <td id="y2"></td>
                            <td>...</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    
    <div class="container instructions">
        <h3>使用说明</h3>
        <ul>
            <li><strong>列表</strong>：从左到右逐个显示函数在x=-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2时的y值</li>
            <li><strong>描点</strong>：从左到右逐个在坐标轴上标记上述9个点的位置</li>
            <li><strong>连线</strong>：从左到右绘制光滑的二次函数曲线（初始状态不显示曲线）</li>
            <li><strong>重置</strong>：恢复到初始状态（仅显示坐标轴）</li>
            <li><strong>拖动条</strong>：调整系数a的值，范围从-3到3</li>
        </ul>
    </div>

    <footer>
        <p>二次函数 y=ax² 动画交互演示 &copy; 2023</p>
    </footer>

    <script>
        // 获取DOM元素
        const canvas = document.getElementById('graphCanvas');
        const ctx = canvas.getContext('2d');
        const aSlider = document.getElementById('aSlider');
        const aValue = document.getElementById('aValue');
        const listBtn = document.getElementById('listBtn');
        const pointsBtn = document.getElementById('pointsBtn');
        const curveBtn = document.getElementById('curveBtn');
        const resetBtn = document.getElementById('resetBtn');
        const valueTable = document.getElementById('valueTable');
        const status = document.getElementById('status');
        
        // 状态变量
        let a = parseFloat(aSlider.value);
        let showPoints = false;
        let showCurve = false;
        let showTable = false;
        let isAnimating = false;
        
        // 坐标轴参数
        const originX = canvas.width / 2;
        const originY = canvas.height / 2;
        let scale = 40; // 每个单位对应的像素数
        const minScale = 20;
        const maxScale = 80;
        const xMin = -canvas.width / (2 * scale);
        const xMax = canvas.width / (2 * scale);
        const yMin = -canvas.height / (2 * scale);
        const yMax = canvas.height / (2 * scale);
        
        // 定义点集
        const points = [-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2];
        
        // 格式化数字，去除小数点后不必要的0
        function formatNumber(num) {
            // 四舍五入到小数点后2位
            const rounded = Math.round(num * 100) / 100;
            // 转换为字符串并去除末尾的0
            return rounded.toString().replace(/(\.0+|0+)$/, '');
        }
        
        // 绘制坐标轴
        function drawAxes() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 绘制坐标轴
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 2;
            
            // X轴
            ctx.beginPath();
            ctx.moveTo(0, originY);
            ctx.lineTo(canvas.width, originY);
            ctx.stroke();
            
            // Y轴
            ctx.beginPath();
            ctx.moveTo(originX, 0);
            ctx.lineTo(originX, canvas.height);
            ctx.stroke();
            
            // 绘制箭头
            // X轴箭头
            ctx.beginPath();
            ctx.moveTo(canvas.width - 10, originY - 5);
            ctx.lineTo(canvas.width, originY);
            ctx.lineTo(canvas.width - 10, originY + 5);
            ctx.fillStyle = '#333';
            ctx.fill();
            
            // Y轴箭头
            ctx.beginPath();
            ctx.moveTo(originX - 5, 10);
            ctx.lineTo(originX, 0);
            ctx.lineTo(originX + 5, 10);
            ctx.fill();
            
            // 绘制刻度
            ctx.fillStyle = '#333';
            ctx.font = '12px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'top';
            
            // X轴刻度
            for (let x = Math.ceil(xMin); x <= Math.floor(xMax); x++) {
                if (x === 0) continue;
                const pixelX = originX + x * scale;
                ctx.beginPath();
                ctx.moveTo(pixelX, originY - 5);
                ctx.lineTo(pixelX, originY + 5);
                ctx.stroke();
                
                ctx.fillText(x.toString(), pixelX, originY + 10);
            }
            
            // Y轴刻度
            ctx.textAlign = 'right';
            ctx.textBaseline = 'middle';
            for (let y = Math.ceil(yMin); y <= Math.floor(yMax); y++) {
                if (y === 0) continue;
                const pixelY = originY - y * scale;
                ctx.beginPath();
                ctx.moveTo(originX - 5, pixelY);
                ctx.lineTo(originX + 5, pixelY);
                ctx.stroke();
                
                ctx.fillText(y.toString(), originX - 10, pixelY);
            }
            
            // 原点标记
            ctx.textAlign = 'right';
            ctx.textBaseline = 'top';
            ctx.fillText('O', originX - 5, originY + 5);
            
            // 坐标轴标签
            ctx.textAlign = 'right';
            ctx.textBaseline = 'bottom';
            ctx.fillText('Y', originX - 10, 20);
            ctx.textAlign = 'left';
            ctx.textBaseline = 'top';
            ctx.fillText('X', canvas.width - 20, originY + 10);
        }
        
        // 计算函数值
        function calculateY(x) {
            return a * x * x;
        }
        
        // 绘制函数图像（光滑曲线）
        function drawFunction() {
            ctx.strokeStyle = '#e74c3c';
            ctx.lineWidth = 3;
            ctx.beginPath();
            
            let firstPoint = true;
            for (let x = xMin; x <= xMax; x += 0.05) {
                const y = calculateY(x);
                const pixelX = originX + x * scale;
                const pixelY = originY - y * scale;
                
                if (firstPoint) {
                    ctx.moveTo(pixelX, pixelY);
                    firstPoint = false;
                } else {
                    ctx.lineTo(pixelX, pixelY);
                }
            }
            
            ctx.stroke();
        }
        
        // 绘制单个点
        function drawPoint(x, delay) {
            setTimeout(() => {
                const y = calculateY(x);
                const pixelX = originX + x * scale;
                const pixelY = originY - y * scale;
                
                ctx.fillStyle = '#3498db';
                ctx.beginPath();
                ctx.arc(pixelX, pixelY, 6, 0, Math.PI * 2);
                ctx.fill();
                
                // 标记坐标（只标记部分点以避免重叠）
                if (x === -2 || x === 0 || x === 2) {
                    ctx.fillStyle = '#2c3e50';
                    ctx.font = '12px Arial';
                    ctx.textAlign = 'center';
                    ctx.textBaseline = 'bottom';
                    ctx.fillText(`(${x}, ${formatNumber(y)})`, pixelX, pixelY - 10);
                }
            }, delay);
        }
        
        // 绘制所有点（带动画）
        function drawPointsAnimated() {
            isAnimating = true;
            pointsBtn.disabled = true;
            status.textContent = "正在绘制点...";
            
            let delay = 0;
            const interval = 400; // 每个点之间的间隔(毫秒)
            
            points.forEach(x => {
                drawPoint(x, delay);
                delay += interval;
            });
            
            setTimeout(() => {
                isAnimating = false;
                pointsBtn.disabled = false;
                status.textContent = "点绘制完成";
            }, delay);
        }
        
        // 绘制曲线（带动画）
        function drawCurveAnimated() {
            isAnimating = true;
            curveBtn.disabled = true;
            status.textContent = "正在绘制曲线...";
            
            ctx.strokeStyle = '#e74c3c';
            ctx.lineWidth = 3;
            
            let currentX = xMin;
            const step = 0.05; // 减小步长使曲线更平滑
            const totalSteps = (xMax - xMin) / step;
            let currentStep = 0;
            
            // 计算动画总时间（4秒）
            const totalTime = 4000; // 4秒
            const stepInterval = totalTime / totalSteps;
            
            ctx.beginPath();
            ctx.moveTo(originX + currentX * scale, originY - calculateY(currentX) * scale);
            
            const animateCurve = setInterval(() => {
                if (currentStep >= totalSteps) {
                    clearInterval(animateCurve);
                    isAnimating = false;
                    curveBtn.disabled = false;
                    status.textContent = "曲线绘制完成";
                    return;
                }
                
                currentX += step;
                const y = calculateY(currentX);
                const pixelX = originX + currentX * scale;
                const pixelY = originY - y * scale;
                
                ctx.lineTo(pixelX, pixelY);
                ctx.stroke();
                
                currentStep++;
            }, stepInterval);
        }
        
        // 更新表格（静默更新，无动画）
        function updateTableSilently() {
            const dataCells = [
                document.getElementById('y-2'),
                document.getElementById('y-1_5'),
                document.getElementById('y-1'),
                document.getElementById('y-0_5'),
                document.getElementById('y0'),
                document.getElementById('y0_5'),
                document.getElementById('y1'),
                document.getElementById('y1_5'),
                document.getElementById('y2')
            ];
            
            points.forEach((x, index) => {
                const y = calculateY(x);
                // 确保x=0时y值显示为0
                if (x === 0 && y === 0) {
                    dataCells[index].textContent = "0";
                } else {
                    dataCells[index].textContent = formatNumber(y);
                }
            });
        }
        
        // 更新表格（带动画）
        function updateTableAnimated() {
            isAnimating = true;
            listBtn.disabled = true;
            status.textContent = "正在生成数值表...";
            
            valueTable.style.display = 'table';
            
            // 获取所有表格单元格
            const headerCells = valueTable.querySelectorAll('thead th');
            const dataCells = [
                document.getElementById('y-2'),
                document.getElementById('y-1_5'),
                document.getElementById('y-1'),
                document.getElementById('y-0_5'),
                document.getElementById('y0'),
                document.getElementById('y0_5'),
                document.getElementById('y1'),
                document.getElementById('y1_5'),
                document.getElementById('y2')
            ];
            
            // 重置单元格样式
            headerCells.forEach(cell => {
                cell.style.opacity = '0';
                cell.style.transform = 'translateY(10px)';
            });
            
            // 获取所有数据行单元格
            const dataRowCells = valueTable.querySelectorAll('tbody td');
            
            dataRowCells.forEach(cell => {
                cell.style.opacity = '0';
                cell.style.transform = 'translateY(10px)';
                // 只清空数据单元格，保留标签和省略号
                if (cell.id) {
                    cell.textContent = '';
                }
            });
            
            // 逐个显示表格单元格
            let delay = 0;
            const interval = 300; // 每个单元格之间的间隔(毫秒)
            
            // 先显示表头
            headerCells.forEach((cell, index) => {
                setTimeout(() => {
                    cell.style.opacity = '1';
                    cell.style.transform = 'translateY(0)';
                    cell.classList.add('fade-in');
                    
                    // 如果是最后一个表头单元格，开始显示数据行
                    if (index === headerCells.length - 1) {
                        // 显示数据行第一列（y=ax²）
                        setTimeout(() => {
                            const yLabelCell = valueTable.querySelector('tbody td:first-child');
                            yLabelCell.style.opacity = '1';
                            yLabelCell.style.transform = 'translateY(0)';
                            yLabelCell.classList.add('fade-in');
                            
                            // 显示数据行第二列（省略号）
                            setTimeout(() => {
                                const firstEllipsisCell = valueTable.querySelector('tbody td:nth-child(2)');
                                firstEllipsisCell.style.opacity = '1';
                                firstEllipsisCell.style.transform = 'translateY(0)';
                                firstEllipsisCell.classList.add('fade-in');
                                
                                // 然后逐个显示数据单元格
                                dataCells.forEach((cellId, dataIndex) => {
                                    setTimeout(() => {
                                        const x = points[dataIndex];
                                        const y = calculateY(x);
                                        // 确保x=0时y值显示为0
                                        if (x === 0 && y === 0) {
                                            cellId.textContent = "0";
                                        } else {
                                            cellId.textContent = formatNumber(y);
                                        }
                                        cellId.style.opacity = '1';
                                        cellId.style.transform = 'translateY(0)';
                                        cellId.classList.add('fade-in');
                                        
                                        // 如果是最后一个数据单元格，显示最后一列省略号
                                        if (dataIndex === dataCells.length - 1) {
                                            setTimeout(() => {
                                                const lastEllipsisCell = valueTable.querySelector('tbody td:last-child');
                                                lastEllipsisCell.style.opacity = '1';
                                                lastEllipsisCell.style.transform = 'translateY(0)';
                                                lastEllipsisCell.classList.add('fade-in');
                                                
                                                setTimeout(() => {
                                                    isAnimating = false;
                                                    listBtn.disabled = false;
                                                    status.textContent = "数值表生成完成";
                                                }, 300);
                                            }, interval);
                                        }
                                    }, (dataIndex + 2) * interval);
                                });
                            }, interval);
                        }, interval);
                    }
                }, index * interval);
            });
        }
        
        // 重绘所有内容
        function redraw() {
            drawAxes();
            
            if (showCurve) {
                drawFunction();
            }
            
            if (showPoints) {
                points.forEach(x => {
                    const y = calculateY(x);
                    const pixelX = originX + x * scale;
                    const pixelY = originY - y * scale;
                    
                    ctx.fillStyle = '#3498db';
                    ctx.beginPath();
                    ctx.arc(pixelX, pixelY, 6, 0, Math.PI * 2);
                    ctx.fill();
                    
                    // 标记坐标（只标记部分点以避免重叠）
                    if (x === -2 || x === 0 || x === 2) {
                        ctx.fillStyle = '#2c3e50';
                        ctx.font = '12px Arial';
                        ctx.textAlign = 'center';
                        ctx.textBaseline = 'bottom';
                        ctx.fillText(`(${x}, ${formatNumber(y)})`, pixelX, pixelY - 10);
                    }
                });
            }
        }
        
        // 初始化
        function init() {
            drawAxes();
            aValue.textContent = a;
        }
        
        // 事件监听
        aSlider.addEventListener('input', function() {
            a = parseFloat(this.value);
            aValue.textContent = a;
            redraw();
            
            // 如果表格正在显示，则更新表格数据
            if (showTable) {
                updateTableSilently();
            }
            
            status.textContent = `系数a已更新为 ${a}`;
        });
        
        listBtn.addEventListener('click', function() {
            if (isAnimating) return;
            showTable = true;
            updateTableAnimated();
        });
        
        pointsBtn.addEventListener('click', function() {
            if (isAnimating) return;
            showPoints = true;
            drawPointsAnimated();
        });
        
        curveBtn.addEventListener('click', function() {
            if (isAnimating) return;
            showCurve = true;
            drawCurveAnimated();
        });
        
        resetBtn.addEventListener('click', function() {
            if (isAnimating) return;
            aSlider.value = 1;
            a = 1;
            aValue.textContent = a;
            showPoints = false;
            showCurve = false;
            showTable = false;
            valueTable.style.display = 'none';
            redraw();
            
            // 如果表格正在显示，则更新表格数据
            if (showTable) {
                updateTableSilently();
            }
            
            status.textContent = '已重置到初始状态（仅显示坐标轴）';
        });
        
        // 添加缩放功能
        canvas.addEventListener('wheel', function(e) {
            e.preventDefault();
            if (isAnimating) return;
            
            const zoomIntensity = 0.1;
            const wheel = e.deltaY < 0 ? 1 : -1;
            const zoom = Math.exp(wheel * zoomIntensity);
            
            scale = Math.min(maxScale, Math.max(minScale, scale * zoom));
            redraw();
            status.textContent = `缩放级别: ${Math.round((scale / minScale) * 100)}%`;
        });
        
        // 初始化
        init();
    </script>
</body>
</html>